﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Input;
using Eniux.Windows.ThreeDEngine.Helper;

namespace Eniux.Windows.ThreeDEngine.Manipulators
{

    /// <summary>
    /// 操作器基类
    /// </summary>
    public abstract class Manipulator : UIElement3D
    {

        #region 属性

        #region 颜色
        /// <summary>
        ///   获取或设置操纵器的颜色
        /// </summary>
        /// <value>The color.</value>
        public Color Color
        {
            get { return (Color)GetValue(ColorProperty); }
            set { SetValue(ColorProperty, value); }
        }
        /// <summary>
        ///   颜色属性.
        /// </summary>
        public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(
            "Color", typeof(Color), typeof(Manipulator), new UIPropertyMetadata(ColorChanged));
        #endregion

        #region 偏移
        /// <summary>
        ///   Gets or sets the offset of the visual (this vector is added to the Position point).
        /// </summary>
        /// <value>The offset.</value>
        public Vector3D Offset
        {
            get { return (Vector3D)GetValue(OffsetProperty); }
            set { SetValue(OffsetProperty, value); }
        }
        /// <summary>
        ///  偏移属性
        /// </summary>
        public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register(
            "Offset",
            typeof(Vector3D),
            typeof(Manipulator),
            new FrameworkPropertyMetadata(
                new Vector3D(0, 0, 0), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PositionChanged));
        #endregion

        #region 位置
        /// <summary>
        ///   获取或设置操纵器的位置
        /// </summary>
        /// <value>The position.</value>
        public Point3D Position
        {
            get { return (Point3D)GetValue(PositionProperty); }
            set { SetValue(PositionProperty, value); }
        }
        /// <summary>
        ///   位置属性
        /// </summary>
        public static readonly DependencyProperty PositionProperty = DependencyProperty.Register(
            "Position",
            typeof(Point3D),
            typeof(Manipulator),
            new FrameworkPropertyMetadata(
                new Point3D(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PositionChanged));
        #endregion

        #region 目标三维转换
        /// <summary>
        ///   获取或设置需要操作目标的三维转换属性
        /// </summary>
        public Transform3D TargetTransform
        {
            get { return (Transform3D)GetValue(TargetTransformProperty); }
            set { SetValue(TargetTransformProperty, value); }
        }
        /// <summary>
        ///   目标三维转换属性
        /// </summary>
        public static readonly DependencyProperty TargetTransformProperty =
            DependencyProperty.Register(
                "TargetTransform",
                typeof(Transform3D),
                typeof(Manipulator),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
        #endregion

        #region 值
        /// <summary>
        ///   获取或设置操纵器的值
        /// </summary>
        /// <value>The value.</value>
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        /// <summary>
        ///   值属性
        /// </summary>
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
            "Value",
            typeof(double),
            typeof(Manipulator),
            new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
        #endregion 

        #endregion

        #region 常量、字段

        /// <summary>
        /// 摄像机
        /// </summary>
        protected ProjectionCamera Camera;

        /// <summary>
        ///   碰撞面板
        /// </summary>
        protected Vector3D HitPlaneNormal;

        /// <summary>
        ///   3D视窗
        /// </summary>
        protected Viewport3D ParentViewport;

        /// <summary>
        ///  模型
        /// </summary>
        protected GeometryModel3D model;

        #endregion

        #region 构造、析构

        /// <summary>
        ///   构造
        /// </summary>
        public Manipulator()
        {
            model = new GeometryModel3D();
            Visual3DModel = model;
            OnGeometryChanged();
        }

        #endregion

        #region 方法

        /// <summary>
        /// Called when Geometry is changed.
        /// </summary>
        /// <param name="d">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.
        /// </param>
        protected static void GeometryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((Manipulator)d).OnGeometryChanged();
        }

        /// <summary>
        /// Projects the point on the hit plane.
        /// </summary>
        /// <param name="p">
        /// The p.
        /// </param>
        /// <param name="hitPlaneOrigin">
        /// The hit Plane Origin.
        /// </param>
        /// <param name="hitPlaneNormal">
        /// The hit plane normal (world coordinate system).
        /// </param>
        /// <returns>
        /// The point in world coordinates.
        /// </returns>
        //protected virtual Point3D? GetHitPlanePoint(Point p, Point3D hitPlaneOrigin, Vector3D hitPlaneNormal)
        //{
        //    return Viewport3DHelper.UnProject(ParentViewport, p, hitPlaneOrigin, hitPlaneNormal);
        //}

        /// <summary>
        /// The on geometry changed.
        /// </summary>
        protected abstract void OnGeometryChanged();

        /// <summary>
        /// The on mouse down.
        /// </summary>
        /// <param name="e">
        /// The event arguments.
        /// </param>
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            //base.OnMouseDown(e);
            //ParentViewport = Visual3DHelper.GetViewport3D(this);
            //Camera = ParentViewport.Camera as ProjectionCamera;
            //HitPlaneNormal = Camera.LookDirection;

            //CaptureMouse();
        }

        /// <summary>
        /// The on mouse up.
        /// </summary>
        /// <param name="e">
        /// The event arguments.
        /// </param>
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);
            ReleaseMouseCapture();
        }

        /// <summary>
        /// Transforms from world to local coordinates.
        /// </summary>
        /// <param name="worldPoint">
        /// The point (world coords).
        /// </param>
        /// <returns>
        /// Transformed vector (local coords).
        /// </returns>
        protected Point3D ToLocal(Point3D worldPoint)
        {
            Matrix3D mat = Visual3DHelper.GetTransform(this);
            mat.Invert();
            var t = new MatrixTransform3D(mat);
            return t.Transform(worldPoint);
        }

        /// <summary>
        /// Transforms from local to world coordinates.
        /// </summary>
        /// <param name="point">
        /// The point (local coords).
        /// </param>
        /// <returns>
        /// Transformed point (world coords).
        /// </returns>
        protected Point3D ToWorld(Point3D point)
        {
            Matrix3D mat = Visual3DHelper.GetTransform(this);
            var t = new MatrixTransform3D(mat);
            return t.Transform(point);
        }

        /// <summary>
        /// Transforms from local to world coordinates.
        /// </summary>
        /// <param name="vector">
        /// The vector (local coords).
        /// </param>
        /// <returns>
        /// Transformed vector (world coords).
        /// </returns>
        protected Vector3D ToWorld(Vector3D vector)
        {
            Matrix3D mat = Visual3DHelper.GetTransform(this);
            var t = new MatrixTransform3D(mat);
            return t.Transform(vector);
        }

        /// <summary>
        /// The color changed.
        /// </summary>
        /// <param name="d">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event arguments.
        /// </param>
        private static void ColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((Manipulator)d).OnColorChanged();
        }

        /// <summary>
        /// Called when position has been changed.
        /// </summary>
        /// <param name="d">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.
        /// </param>
        private static void PositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((Manipulator)d).OnPositionChanged();
        }

        /// <summary>
        /// The on color changed.
        /// </summary>
        private void OnColorChanged()
        {
            model.Material = MaterialHelper.CreateMaterial(Color);
            model.BackMaterial = model.Material;
        }

        /// <summary>
        /// Called when position is changed.
        /// </summary>
        private void OnPositionChanged()
        {
            Transform = new TranslateTransform3D(
                Position.X + Offset.X, Position.Y + Offset.Y, Position.Z + Offset.Z);
        }

        #endregion
    }
}
